package com.vhall.component.service.lottery.delay;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vhall.component.dao.livecolumn.LiveColumnAttentionMapper;
import com.vhall.component.dao.lottery.LotteryJoinsMapper;
import com.vhall.component.dao.lottery.LotteryMapper;
import com.vhall.component.dao.lottery.LotteryRecordMapper;
import com.vhall.component.dao.lottery.LotteryUserMapper;
import com.vhall.component.dao.room.RoomJoinsMapper;
import com.vhall.component.entity.livecolumn.LiveColumnAttention;
import com.vhall.component.entity.lottery.entity.LotteryEntity;
import com.vhall.component.entity.lottery.entity.LotteryJoinsEntity;
import com.vhall.component.entity.lottery.entity.LotteryRecordEntity;
import com.vhall.component.entity.lottery.entity.LotteryUserEntity;
import com.vhall.component.entity.lottery.vo.LotteryRecordEndVO;
import com.vhall.component.entity.lottery.vo.LotteryV4AddReqVO;
import com.vhall.component.entity.room.dto.RoomExtendsDTO;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.entity.RoomJoinsEntity;
import com.vhall.component.entity.room.vo.QueryRoomJoinsReqVO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.framework.common.support.RequestUrlThreadLocal;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.core.service.OssService;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.livecolumn.LiveColumnService;
import com.vhall.component.service.lottery.LotteryJoinsService;
import com.vhall.component.service.lottery.LotteryRecordRepository;
import com.vhall.component.service.lottery.common.Consts;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.vote.delay.DelayEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author jingwen.li
 */
@Slf4j
@Component
public class LotteryDelayTaskRunner implements InitializingBean {

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private DelayQueue<LotteryDelayEvent> lotteryDelayQueue;
    @Autowired
    private OssService ossService;
    @Autowired
    private LotteryMapper lotteryMapper;
    @Autowired
    private PaasService paasService;
    @Autowired
    private RoomInnerService roomInnerService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private LotteryRecordRepository lotteryRecordRepository;
    @Autowired
    private LotteryJoinsService lotteryJoinsService;
    @Autowired
    private RoomJoinsMapper roomJoinsMapper;
    @Autowired
    private LotteryRecordMapper lotteryRecordMapper;
    @Autowired
    private LiveColumnService liveColumnService;

    @Override
    public void afterPropertiesSet() throws Exception {
        getCacheDelayCache();
        threadPoolTaskExecutor.execute(() -> {
            while (true) {
                LotteryDelayEvent event = lotteryDelayQueue.poll();
                if (event == null) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        log.info("睡眠被打断:{}", e);
                    }
                    continue;
                }
                if (Boolean.FALSE.equals(stringRedisTemplate.opsForValue().setIfAbsent(RedisKey.LOTTERY_DELAY_ID + event.getLotteryId(), "1", 1, TimeUnit.MINUTES))) {
                    continue;
                }
                log.info("房间:{}的抽奖:{},倒计时结束,公布抽奖结果", event.getRoomId(), event.getLotteryId());
                LotteryEntity lotteryEntity = lotteryMapper.selectById(event.getLotteryId());
                LotteryV4AddReqVO reqParam = event.getReqParam();
                String requestUrl = event.getRequestUrl();
                try {
                    JSONArray winners = randomWinners(reqParam, lotteryEntity.getId());
                    lotteryEntity.setActualLotteryNumber(winners.size());
                    lotteryEntity.setUpdatedAt(LocalDateTime.now());
                    lotteryMapper.updateById(lotteryEntity);

                    recordLotteryWinner(winners, lotteryEntity);
                    if (StrUtil.isNotEmpty(requestUrl)){
                        RequestUrlThreadLocal.setRequestUrl(requestUrl);
                    }
                    Thread.sleep(300);
                } catch (Exception e) {
                    log.error("抽奖异常:", e);
                } finally {
                    pushMsg(event.getLotteryId(), event.getRoomId(), lotteryEntity);
                    String lockKey = RedisKey.LOTTERY_ADD_LOCK + reqParam.getRoomId();
                    redisTemplate.delete(lockKey);
                    redisTemplate.opsForHash().delete(RedisKey.INTERACT_TOOL_RECORDS + reqParam.getRoomId(), "lottery_status", "lottery_id", "lottery_creator_id");
                }


            }
        });

    }

    private JSONArray randomWinners(LotteryV4AddReqVO req, Long lotteryId) {
        //随机抽取中奖者
        JSONArray randomWinners = new JSONArray();
        String roomId = req.getRoomId();
        String key = RedisKey.LOTTERY_RANGE_ACCOUNTS + roomId;
        initLotteryInitUser(key, req, lotteryId);

        Integer lotteryNumber = req.getLotteryNumber();
        log.info("开始抽奖[{}]:", lotteryId);
        //  redis里面 随机 抽取 id
        if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
            log.info("开始抽奖[{}],抽奖参与者为空", lotteryId);
            return randomWinners;
        }
        if (redisTemplate.opsForSet().size(key) == 0) {
            log.info("开始抽奖[{}],抽奖参与者为空", lotteryId);
            return randomWinners;
        }
        Long size = redisTemplate.opsForSet().size(key);
        if (lotteryNumber > size) {
            lotteryNumber = Math.toIntExact(size);
        }
        while (lotteryNumber > 0) {
            List<Integer> userIdList = redisTemplate.opsForSet().randomMembers(key, lotteryNumber);
            if (userIdList == null || userIdList.size() == 0) {
                log.info("开始抽奖[{}],被抽中用户为空", lotteryId);
                break;
            }
            for (Integer userId : userIdList) {
                log.info("开始抽奖[{}],被抽中用户ID:{}", lotteryId, userId);
            }
//            log.info("开始抽奖[{}],被抽中用户ID:{}", lotteryId, userIdList);

            //移除中将人id
            redisTemplate.opsForSet().remove(key, userIdList);

            QueryRoomJoinsReqVO reqVO = new QueryRoomJoinsReqVO();
            reqVO.setRoomId(roomId);
            StringJoiner stringJoiner = new StringJoiner(",");
            for (Integer memberId : userIdList) {
                stringJoiner.add(String.valueOf(memberId));
            }
            reqVO.setAccountIds(stringJoiner.toString());
            List<RoomJoinsEntityDTO> srjedtos = roomInnerService.getRoomJoinsList(reqVO);
            if (CollUtil.isEmpty(srjedtos)) {
                log.info("开始抽奖[{}],抽中的用户ID:{}，roomJoins为空", lotteryId, userIdList);
                break;
            }
            for (RoomJoinsEntityDTO roomJoinsEntity : srjedtos) {
                if (roomJoinsEntity.getIsLotteryWinner() != null && roomJoinsEntity.getIsLotteryWinner() == 1) {
                    redisTemplate.opsForSet().remove(key, roomJoinsEntity.getAccountId());
                    continue;
                }
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("lottery_user_id", roomJoinsEntity.getAccountId());
                jsonObject.put("lottery_user_nickname", roomJoinsEntity.getNickname());
                jsonObject.put("lottery_user_avatar", roomJoinsEntity.getAvatar());
//                jsonObject.put("lottery_user_userName", roomJoinsEntity.getUsername());
//                jsonObject.put("lottery_user_phone", roomJoinsEntity.getUsername());
                jsonObject.put("preset", 0);
                lotteryNumber--;
                redisTemplate.opsForSet().remove(key, roomJoinsEntity.getAccountId());
                randomWinners.add(jsonObject);
                log.info("开始抽奖[{}],保存抽中的用户信息:{}", lotteryId, roomJoinsEntity.getAccountId());
            }

        }
        return randomWinners;
    }

    private void initLotteryInitUser(String key, LotteryV4AddReqVO req, Long lotteryId) {
        log.info("初始化参与抽奖[{}]的用户---start", lotteryId);
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(req.getRoomId());

        LambdaQueryWrapper<RoomJoinsEntity> where = Wrappers.lambdaQuery();
        where.eq(RoomJoinsEntity::getRoomId, roomInfo.getRoomId())
                .ne(RoomJoinsEntity::getRoleName, com.vhall.component.plugin.common.constant.RoomConstant.AUDIENCE)
                .orderByDesc(RoomJoinsEntity::getRoleName);

        // 主持人、嘉宾/助理
        List<RoomJoinsEntity> listSpecial = roomJoinsMapper.selectList(where);
        Set<Integer> listSpecialSet = listSpecial.stream().map(RoomJoinsEntity::getAccountId).collect(Collectors.toSet());

        Set<Integer> onlineUserSet = new HashSet<>();
        int onlinePage = 1;
        while (true) {
            List<Integer> userIdList = paasService.getUserIdList(roomInfo.getChannelId(), onlinePage, 500);
            if (CollUtil.isNotEmpty(userIdList)) {
                onlineUserSet.addAll(userIdList);
                if (userIdList.size() < 500) {
                    break;
                }
            } else {
                break;
            }
            onlinePage++;
        }
        // 除去 主持人、嘉宾/助理
        onlineUserSet.removeAll(listSpecialSet);

        Set<Integer> joinUserList = new HashSet<>();

        if (req.getLotteryRule() == 1) {
            if (onlineUserSet.size() > 0) {
                redisTemplate.opsForSet().add(key, onlineUserSet.toArray());
                joinUserList.addAll(onlineUserSet);
            }
        } else {
            // 栏目订阅用户
            if (req.getLotteryType() == 2) {
                log.info("初始化参与抽奖[{}]-栏目订阅用户", lotteryId);
                for (Integer accountId : onlineUserSet) {
                    if (liveColumnService.isSubscribe(roomInfo.getColumnId(), accountId)) {
                        redisTemplate.opsForSet().add(key, accountId);
                        joinUserList.add(accountId);
                    }
                }
            } else if (req.getLotteryType() == 1) {
                // 点赞用户
                log.info("初始化参与抽奖[{}]-点赞用户", lotteryId);
                String likeKey = RedisKey.ROOM_LIKE_ACCOUNT + req.getRoomId();
                if (redisTemplate.hasKey(likeKey)) {
                    for (Integer userId : onlineUserSet) {
                        Boolean member = redisTemplate.opsForSet().isMember(likeKey, userId);
                        if (Boolean.TRUE.equals(member)) {
                            joinUserList.add(userId);
                            redisTemplate.opsForSet().add(key, userId);
                        }
                    }
                }
            } else if (req.getLotteryType() == 3) {
                // 发表指定评论用户
                log.info("初始化参与抽奖[{}]-发表指定评论用户", lotteryId);
                String commentJoinKey = RedisKey.KEY_LOTTERY_COMMENT_JOIN + lotteryId;
                if (Boolean.TRUE.equals(redisTemplate.hasKey(commentJoinKey))) {
                    for (Integer userId : onlineUserSet) {
                        Boolean member = stringRedisTemplate.opsForSet().isMember(commentJoinKey, String.valueOf(userId));
                        log.info("初始化参与抽奖[{}]-发表指定评论用户:{}, isJoin:{}", lotteryId, userId, member);
                        if (Boolean.TRUE.equals(member)) {
                            joinUserList.add(userId);
                            redisTemplate.opsForSet().add(key, userId);
                        }
                    }
                }
                String commentKey = RedisKey.KEY_LOTTERY_TYPE_COMMENT + req.getRoomId();
                redisTemplate.delete(commentKey);
            }
        }

        threadPoolTaskExecutor.execute(() -> {
            // 保存参与抽奖的用户
            List<LotteryJoinsEntity> list = new ArrayList<>();
            for (Integer id : joinUserList) {
                LotteryJoinsEntity joinsEntity = new LotteryJoinsEntity();
                joinsEntity.setAccountId(id);
                joinsEntity.setLotteryId(Math.toIntExact(lotteryId));
                joinsEntity.setRoomId(roomInfo.getRoomId());
                joinsEntity.setIlId(roomInfo.getIlId());
                list.add(joinsEntity);
                if (list.size() == 500) {
                    lotteryJoinsService.saveBatch(list);
                    list = new ArrayList<>();
                }
            }
            if (CollUtil.isNotEmpty(list)) {
                lotteryJoinsService.saveBatch(list);
            }

        });
        log.info("初始化参与抽奖[{}]的用户数:{}", lotteryId, redisTemplate.opsForSet().size(key));
        log.info("初始化参与抽奖[{}]的用户---end", lotteryId);
    }

    private void recordLotteryWinner(JSONArray winners, LotteryEntity lottery) {
        if (CollectionUtils.isEmpty(winners)) {
            return;
        }
        //批量保存抽奖记录
        batchSaveLotteryRecord(winners, lottery);

        List<Integer> userIdList = new ArrayList<>();
        winners.forEach(o -> {
            JSONObject jsonObject = (JSONObject) o;
            userIdList.add(jsonObject.getInteger("lottery_user_id"));
        });
        String key = RedisKey.LOTTERY_RANGE_ACCOUNTS + lottery.getSourceId();

//        if (2 == lotteryRule) {
//            //自定义抽奖，更新lotteryUser的is_winner
//            LambdaUpdateWrapper<LotteryUserEntity> wrapper = Wrappers.<LotteryUserEntity>lambdaUpdate()
//                    .set(LotteryUserEntity::getIsWinner, 1)
//                    .set(LotteryUserEntity::getUpdatedAt, LocalDateTime.now())
//                    .eq(LotteryUserEntity::getAppId, lottery.getAppId())
//                    .eq(LotteryUserEntity::getRoomId, lottery.getSourceId())
//                    .in(LotteryUserEntity::getId, userIdList);
//            lotteryUserMapper.update(null, wrapper);

//        } else {
        RoomJoinsEntityDTO roomJoinsEntityDTO = new RoomJoinsEntityDTO();
        roomJoinsEntityDTO.setIsLotteryWinner(1);
        roomInnerService.updateLotteryWinner(roomJoinsEntityDTO, lottery.getSourceId(), userIdList);
//        }
        //清空redis中的数据
        redisTemplate.delete(key);
    }

    private void batchSaveLotteryRecord(JSONArray winners, LotteryEntity lottery) {
        List<LotteryRecordEntity> list = new ArrayList<>();
        for (Object o : winners) {
            JSONObject jsonObject = (JSONObject) o;
            LotteryRecordEntity lotteryRecordEntity = new LotteryRecordEntity();
            lotteryRecordEntity.setAppId(lottery.getAppId());
            lotteryRecordEntity.setLotteryId(lottery.getId());
            lotteryRecordEntity.setCreatorId(lottery.getCreatorId());
            lotteryRecordEntity.setCreatedAt(LocalDateTime.now());
            lotteryRecordEntity.setUpdatedAt(LocalDateTime.now());
            lotteryRecordEntity.setLotteryUserAvatar(jsonObject.getString("lottery_user_avatar"));
            lotteryRecordEntity.setLotteryUserId(jsonObject.getInteger("lottery_user_id").toString());
            lotteryRecordEntity.setLotteryUserNickname(jsonObject.getString("lottery_user_nickname"));

//            lotteryRecordEntity.setLotteryUserName(jsonObject.getString("lottery_user_userName"));
//            lotteryRecordEntity.setLotteryUserPhone(jsonObject.getString("lottery_user_phone"));
//            lotteryRecordEntity.setLotteryUserRemark("");
            lotteryRecordEntity.setSourceId(lottery.getSourceId());
            lotteryRecordEntity.setPreset(jsonObject.getInteger("preset"));
            list.add(lotteryRecordEntity);
        }
        if (CollectionUtils.isNotEmpty(list)) {
            lotteryRecordRepository.saveBatch(list);

        }

    }


    private void pushMsg(Long lotteryId, String roomId, LotteryEntity lottery) {
        try {
            if (Consts.LOTTERY_STATUS_END == lottery.getLotteryStatus()) {
                throw new BusinessException(BizErrorCode.COMP_LOTTERY_HAS_ENDED);
            }
            RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(roomId);
            if (roomsEntityDTO == null) {
                throw new BusinessException(BizErrorCode.EMPTY_ROOM);
            }
            if (!StringUtils.equalsAnyIgnoreCase(lottery.getSourceId(), roomId)) {
                throw new BusinessException(BizErrorCode.COMP_LOTTERY_END_PARAM_ERROR);
            }
            lottery.setLotteryStatus(Consts.LOTTERY_STATUS_END);
            lottery.setUpdatedAt(LocalDateTime.now());
            lotteryMapper.updateById(lottery);
            //组装响应类
            List<LotteryRecordEntity> userRspVos = lotteryRecordMapper.selectList(Wrappers.<LotteryRecordEntity>lambdaQuery().eq(LotteryRecordEntity::getLotteryId, lottery.getId()));

            List<LotteryRecordEndVO> lrevos = new ArrayList<>();
            if (CollUtil.isNotEmpty(userRspVos)) {
                for (LotteryRecordEntity lotteryRecordEntity : userRspVos) {
                    LotteryRecordEndVO lotteryRecordVO;
                    lotteryRecordVO = new LotteryRecordEndVO();
                    BeanUtils.copyProperties(lotteryRecordEntity, lotteryRecordVO);
                    lrevos.add(lotteryRecordVO);
                }
            }

            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> content = new HashMap<>();

            content.put("lottery_id", lotteryId);
            content.put("lottery_title", lottery.getTitle());
            if (CollUtil.isNotEmpty(lrevos)){
                content.put("lottery_winners", lrevos);
            }else {
                content.put("lottery_winners", new ArrayList<>());
            }

            String contentPath;
            try {
                contentPath = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(content);
            } catch (JsonProcessingException e) {
                log.error("结束抽奖失败", e);
                throw new BusinessException(BizErrorCode.COMP_LOTTERY_END_FAILURE);
            }
            String fileName = "lottery/" +lottery.getId() + ".json";
            String path = ossService.upload(IOUtils.toInputStream(contentPath, StandardCharsets.UTF_8), fileName, "application/json");
            if (StrUtil.isEmpty(path)) {
                path = "";
                log.info("抽奖结果上传失败:{}", lotteryId);
            }
            stringRedisTemplate.opsForValue().set(Consts.LOTTERY_WINNERS_JSON_URL + lottery.getId(), path, 86400, TimeUnit.SECONDS);

            RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
            Map<String, Object> json = new HashMap<>(4);
            json.put("type", "lottery_result_notice_guoxin");
            json.put("room_id", roomId);                            // 房间ID
            json.put("lottery_id", lotteryId);                      // 抽奖ID
            json.put("lottery_title", lottery.getTitle());          // 抽奖标题(奖品名称)
            json.put("extension", lottery.getExtension());          // 抽奖扩展信息
            json.put("prize_image", lottery.getAwardSnapshoot());   // 奖品图片
            json.put("lottery_rule", lottery.getLotteryRule());     // 抽奖规则 1：随机抽奖  2：满足条件的抽奖
            json.put("lottery_type", lottery.getLotteryType());     // 参与抽奖人员范围 1 参与点赞的用户 2 关注当前直播栏目的用户 3 发表指定评论的用户
            json.put("lottery_prize_type", lottery.getLotteryPrizeType()); // 奖品类型 1:实物奖品 2: 优惠券
            json.put("lottery_winners_url", path);                  // 中奖者名单文件下载链接
            if (lottery.getLotteryPrizeType() != null && lottery.getLotteryPrizeType() == 2) {
                // 优惠券
                JSONObject voucherJSON = new JSONObject();
                voucherJSON.put("voucher_name", "优惠券名称");    // 优惠券名称
                voucherJSON.put("expire_time", "yyyy-MM-dd");     // 过期时间
                voucherJSON.put("channel", "来自于活动");         // 优惠券来源
                voucherJSON.put("voucher_type", "体验券");    // 1-满减券，2-折扣券 , 3-体验券
                json.put("voucher", voucherJSON);
            }
            paasService.sendMessage(roomInfo.getChannelId(), JsonUtil.toJsonString(json));

        } catch (Exception e) {

        } finally {
            redisTemplate.opsForHash().delete(RedisKey.INTERACT_TOOL_RECORDS + roomId, "lottery_status", "lottery_id", "lottery_creator_id");
        }
    }

    private void getCacheDelayCache() {
        try {
            if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisKey.LOTTERY_DELAY_QUEUE))) {
                List<String> range = stringRedisTemplate.opsForList().range(RedisKey.LOTTERY_DELAY_QUEUE, 0, -1);
                if (CollUtil.isEmpty(range)) {
                    return;
                }
                range.forEach(s -> {
                    LotteryDelayEvent event = JSON.parseObject(s, LotteryDelayEvent.class);
                    lotteryDelayQueue.add(event);
                });

            }
        } catch (Exception e) {
            log.info("初始化缓存抽奖数据失败:{}", e);
        }

    }
}
